﻿using NUnit.Framework;
using Sasa.QualityTools.CodeAnalysis.Core.Requests;
using Sasa.QualityTools.CodeAnalysis.Core.Services.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.IO;

namespace Sasa.QualityTools.CodeAnalysis.Core.Tests.Unit.Services.CodeAnalysis
{
    [TestFixture]
    public class AssemblyAnalyzerTest
    {
        private static readonly string ResourceDirectory = Path.Combine("Resources", "Services", "CodeAnalysis", "AssemblyAnalyzer");

        private AssemblyAnalyzer testee;

        private AssemblyAnalysisRequest request;


        [SetUp]
        public void SetUp()
        {
            request = new AssemblyAnalysisRequest();
            request.FxCopCmdPath = Path.Combine(ResourceDirectory, "FxCopCmd.exe");
            request.AssemblyPath = Path.Combine(ResourceDirectory, "Target.dll");
            request.EnableSearchGac = true;
            request.IgnoreGeneratedCode = true;
            request.Timeout = 120;
            request.RuleSetPath = Path.Combine(ResourceDirectory, "Custom.ruleset");

            testee = new AssemblyAnalyzer(request);
        }

        [Test]
        [Description("FxCopReportValueConverter should return an instance of the DefaultFxCopReportValueConverter class.")]
        public void TestConverterType()
        {
            Assert.IsInstanceOf<DefaultFxCopReportValueConverter>(testee.FxCopReportValueConverter);
        }

        [Test]
        [Description("Output should return correct output path.")]
        public void TestOutputPath()
        {
            Assert.AreEqual(Path.Combine(ResourceDirectory, "CodeAnalysisReport.xml"), testee.OutputPath);
        }

        [Test]
        [Description("Output should throw an exception when an analysis target that is specified by Target of request doesn't exist.")]
        public void TestOutputPathWithInvalidRequest()
        {
            request.AssemblyPath = Path.Combine(ResourceDirectory, "NotExist.dll");
            Assert.Throws<InvalidOperationException>(() => { var path = testee.OutputPath; });
        }

        [Test]
        [Description("CreateFxCopCmdOptions should return correct FxCopCmd options.")]
        public void TestCreateFxCopCmdOptions()
        {
            var actual = testee.CreateFxCopCmdOptions();

            var expected = new string[] { "/f:" + '"' + request.AssemblyPath + '"', "/o:" + '"' + testee.OutputPath + '"', "/c", "/q",
                "/to:" + request.Timeout, "/gac", "/igc", "/rs:=" + '"' + request.RuleSetPath + '"' };
            CollectionAssert.AreEqual(expected, actual);
        }

        [Test]
        [Description("CreateFxCopCmdOptions should set 0 to '/to' when Timeout of request is negative.")]
        public void TestCreateFxCopCmdOptionsWithNegativeTimeout()
        {
            request.Timeout = -120;

            var actual = testee.CreateFxCopCmdOptions();

            var expected = new string[] { "/f:" + '"' + request.AssemblyPath + '"', "/o:" + '"' + testee.OutputPath + '"', "/c", "/q",
                "/to:0", "/gac", "/igc", "/rs:=" + '"' + request.RuleSetPath + '"' };
            CollectionAssert.AreEqual(expected, actual);
        }

        [Test]
        [Description("CreateFxCopCmdOptions shouldn't set '/gac' when EnableSearchGac of request is false.")]
        public void TestCreateFxCopCmdOptionsWithoutGAC()
        {
            request.EnableSearchGac = false;

            var actual = testee.CreateFxCopCmdOptions();

            var expected = new string[] { "/f:" + '"' + request.AssemblyPath + '"', "/o:" + '"' + testee.OutputPath + '"', "/c", "/q",
                "/to:" + request.Timeout, "/igc", "/rs:=" + '"' + request.RuleSetPath + '"' };
            CollectionAssert.AreEqual(expected, actual);
        }

        [Test]
        [Description("CreateFxCopCmdOptions shouldn't set '/igc' when IgnoreGeneratedCode of request is false.")]
        public void TestCreateFxCopCmdOptionsWithoutIGC()
        {
            request.IgnoreGeneratedCode = false;

            var actual = testee.CreateFxCopCmdOptions();

            var expected = new string[] { "/f:" + '"' + request.AssemblyPath + '"', "/o:" + '"' + testee.OutputPath + '"', "/c", "/q",
                "/to:" + request.Timeout, "/gac", "/rs:=" + '"' + request.RuleSetPath + '"' };
            CollectionAssert.AreEqual(expected, actual);
        }

        [Test]
        [Description("CreateFxCopCmdOptions shouldn't set '/rs' when ruleset that is specified by RuleSetPath of request doesn't exist.")]
        public void TestCreateFxCopCmdOptionsWithoutRuleSet()
        {
            request.RuleSetPath = Path.Combine(ResourceDirectory, "NotExist.ruleset");

            var actual = testee.CreateFxCopCmdOptions();

            var expected = new string[] { "/f:" + '"' + request.AssemblyPath + '"', "/o:" + '"' + testee.OutputPath + '"', "/c", "/q",
                "/to:" + request.Timeout, "/gac", "/igc" };
            CollectionAssert.AreEqual(expected, actual);
        }

        [Test]
        [Description("CreateFxCopCmdOptions should throw an exception when assembly that is specified by AssemblyPath of request doesn't exist.")]
        public void TestCreateFxCopCmdOptionsWithInvalidRequest()
        {
            request.AssemblyPath = Path.Combine(ResourceDirectory, "NotExist.dll");

            Assert.Throws<InvalidOperationException>(() => testee.CreateFxCopCmdOptions());
        }
    }
}
